(*
  Obj module
  -------------------------------
  Authors:
  Antoine Jackson

  -------------------------------
  This module makes the Obj file.

  First, it takes the image with the grid and the edges and computes the points

*)


type point = 
    {
      mutable x : float;
      mutable y : float;
      mutable z : float;
    }

let point_from a b c =
  begin
    {x=a;y=b;z=c}
  end

type triangle =
    {
      mutable a : int;
      mutable b : int;
      mutable c : int;
    }

let triangle_from i j k =
  {a=i;b=j;c=k}

let points : point array ref = ref ([||]) (*vertices*)
let triangles : triangle array ref = ref ([||]) (*triangles indices*)
let pointsm : int array array ref = ref ([||])


let triangles_list : triangle list ref =  ref []


let heights_list : ((int*int*int)*float) list ref = ref []

let initialize_heights img oil =
  begin
    heights_list := [];
    heights_list := Img.color_find img oil;
(*    create_txtColorFile (); *)
  end

let rec get_height color = function
  | [] -> 0.
  | (hcol,h)::l when color = hcol -> h
  | (hcol,h)::l -> get_height color l


let process_image img_path step =                                                                   
  begin                                                                                                                                                                                

    let img = Sdlloader.load_image img_path in                                                
    let (w,h) = Img.get_dims img in                                                            
    for x=0 to w do
      for y=0 to h do                                                                              
	begin                                            
	  Sdlvideo.put_pixel_color img x y (0,0,0);
	  if (x mod step = 0) && (y mod step =0) then
	    Sdlvideo.put_pixel_color img x y (255,255,0);
        end                                                                                                                                                                            
      done                                                                                                                                                                             
    done;                                                                                                                                                                              
    Sdlvideo.save_BMP img "grided.bmp";                                                                                                                                                
  end

let compute_points img_path step = 
  begin

    process_image "out.bmp" step;
    let img = Sdlloader.load_image "grided.bmp" in
    let ori = Sdlloader.load_image img_path in

    let (w,h) = Img.get_dims img in
    let i = ref 0 in
    for x=1 to w-1 do
      let btw_array : int array ref = ref [||] in
      btw_array := [||];
      i := 0;
      for y=1 to h-1 do
	let (r,g,b) = Sdlvideo.get_pixel_color img x y in
	let (e,f,h) = Sdlvideo.get_pixel_color ori x y in
	if r>0 then
	  begin
	    let pt = {x=(float)x ;y=(float)(y) ;z=(get_height (e,f,h) !heights_list) } in
	    
	    points := Array.append !points (Array.make 1 pt);
	    if (g=255) then
	      begin
		btw_array := Array.append !btw_array (Array.make 1 (Array.length !points-1));
	      end
	    else
(*	      points_to_add := Array.append !points_to_add (Array.make 1 (Array.length !points-2)); *)
	    incr i;
	  end
	
      done;
      if (Array.length !btw_array > 0) then
	begin
	  pointsm := Array.append !pointsm (Array.make 1 !btw_array);
	end
    done
  end


(*
  Geometrical functions
*)


let hor_proj point = 
  match point with
    | {x=a;y=b;z=c} -> {x=a;y=b;z=0.}

let dot p1 p2 = 
  p1.x*.p2.x +. p1.y*.p2.y +. p1.z*.p2.z


let vector_from a b =
  begin
    {x=b.x-.a.x;y=b.y-.a.y;z=b.z-.a.z}
  end

let norm vector = 
  sqrt (vector.x*.vector.x +. vector.y*.vector.y +. vector.z*.vector.z)

let array_contains array x = 
  begin
    print_string "\n---";
    let answer = ref false in
    answer := false;
    for i=0 to Array.length array - 1 do
      answer :=  x = array.(i) || !answer;
      print_string ("\n"^string_of_int x ^ "="^ string_of_int array.(i))
    done;
    print_string "\n";
    !answer
  end

let smooth points_array fact =
  begin
    for i=0 to fact do
      for x=0 to (Array.length points_array-1) do
        for y=0 to ((Array.length points_array.(x)-1)) do

          if x >= 1 && y >=1  &&
            x <= (Array.length points_array-2) &&
            y <= Array.length points_array.(x)-2  then
            begin
              !points.(points_array.(x).(y)).z <-(

                !points.(points_array.(x-1).(y-1)).z +.
                  !points.(points_array.(x-1).(y)).z +.
                  !points.(points_array.(x-1).(y+1)).z +.
                  !points.(points_array.(x).(y-1)).z +.
                  25. *. !points.(points_array.(x).(y)).z +.
                  !points.(points_array.(x).(y+1)).z +.
                  !points.(points_array.(x+1).(y-1)).z +.
                  !points.(points_array.(x+1).(y)).z +.
                  !points.(points_array.(x+1).(y+1)).z
              )/.33.;
            end
          else
            ()(*!points.(points_array.(x).(y)).z <- 0.;*)
        done;
      done;
    done;
  end


(*
  Triangulation
*)

let triangulate img_path step sth = 
  begin
    compute_points img_path step;
    smooth !pointsm sth;


    
    for i = 0 to Array.length !pointsm - 2 do
      for j=0 to Array.length !pointsm.(i) - 2 do
	let t1 = {a=(!pointsm.(i).(j)); b=(!pointsm.(i+1).(j)); c=(!pointsm.(i+1).(j+1)) } in
	let t2 = {a=(!pointsm.(i).(j)); b=(!pointsm.(i+1).(j+1)); c=(!pointsm.(i).(j+1)) } in
	triangles_list := t1::t2::!triangles_list;
      done;

    done;

  end

(*
  Writing in Obj
  We've got the points
  We've got the triangle list
*)

let rec print_triangles = function
  | [] -> ""
  | e::l -> "f "^string_of_int(e.a+1)^" "
    ^ string_of_int (e.b+1)^" "
    ^ string_of_int (e.c+1)^" \n" ^ (print_triangles l)

let rec write_color_file = function
  | [] -> ""
  | ((r,g,b),h)::l -> (string_of_float h)^";"
    ^(string_of_int r)^";"
    ^(string_of_int g)^";"
    ^(string_of_int b)^"\n"
    ^(write_color_file l)


let write_obj img_path obj_path step sth =
  begin
    triangles_list := [];
    points := ([||]); (*vertices*)
    triangles := ([||]); (*triangles indices*)
    pointsm := ([||]);
    triangulate img_path step sth;
    let content = ref "" in
    let oc = open_out obj_path in
    content := "";
    Printf.fprintf oc "%s\n" "-";
    (*
      writing the points
    *)
    let out = open_out_gen [Open_wronly; Open_append; Open_creat; Open_text] 0o666 obj_path in
    for i=0 to Array.length !points -1 do
      content := !content^"v ";
      content := !content^string_of_float(!points.(i).x)^" ";
      content := !content^string_of_float(!points.(i).y)^" ";
      content := !content^string_of_float(!points.(i).z)^" \n";
      if (i mod 100 = 0) then
	begin

	  output_string out !content;

	  content := "";
	end
    done;

    output_string out !content;
    content := "";

    (* 
       writing the triangles
    *)
    
    content := !content^(print_triangles !triangles_list);
    

    output_string out !content;
    close_out out;

    let odc = open_out "color_heights" in
    let cfile_cont = write_color_file !heights_list in
    Printf.fprintf odc "%s\n" cfile_cont;
  end


